package online.chazi.netkit.channel;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import online.chazi.netkit.NetkitContext;
import online.chazi.netkit.NetkitHandler;
import online.chazi.netkit.messaging.Message;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 说明：作者很懒，什么都没留下
 * Created by 叉子同学 on 2020-04-24 14:17
 */
@Slf4j
public class NetkitWebSocketPipelineFactory extends ChannelInitializer<SocketChannel> {

    private NetkitContext context;
    private String path = "/websocket";

    public NetkitWebSocketPipelineFactory(NetkitContext context) {
        this.context = context;
    }

    public NetkitWebSocketPipelineFactory(NetkitContext context, String path) {
        this.context = context;
        this.path = path;
    }

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        long timeoutMillis = context.getSessionTimeout().toMillis();
        socketChannel.pipeline()
                // HTTP请求的解码和编码
                .addLast(new HttpServerCodec())
                // 把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse，
                // 原因是HTTP解码器会在每个HTTP消息中生成多个消息对象HttpRequest/HttpResponse,HttpContent,LastHttpContent
                .addLast(new HttpObjectAggregator(65536))
                // 主要用于处理大数据流，比如一个1G大小的文件如果你直接传输肯定会撑暴jvm内存的; 增加之后就不用考虑这个问题了
                .addLast(new ChunkedWriteHandler())
                // WebSocket数据压缩
                .addLast(new WebSocketServerCompressionHandler())
                // 协议包长度限制
                .addLast(new WebSocketServerProtocolHandler(this.path, null, true, 10240))

                // 接收到信息进行协议包解码。只处理文本格式的内容。
                .addLast(new MessageToMessageDecoder<TextWebSocketFrame>() {
                    @Override
                    protected void decode(ChannelHandlerContext ctx, TextWebSocketFrame frame, List<Object> recv) throws Exception {
                        try {
                            JSONObject jsonObject = JSON.parseObject(frame.text());
                            String body = jsonObject.getString("body");
                            Message message;
                            if (body != null) {
                                message = new Message(
                                        jsonObject.getJSONObject("head").getIntValue("id"),
                                        body
                                );
                            } else {
                                message = new Message(
                                        jsonObject.getJSONObject("head").getIntValue("id")
                                );
                            }
                            recv.add(message);
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                        }
                    }
                })

                // 发送内容先编码
                .addLast(new MessageToMessageEncoder<Message>() {
                    @Override
                    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, List<Object> out) throws Exception {
                        JSONObject jsonObject = new JSONObject();
                        Map<String, Object> head = new HashMap<>();
                        head.put("id", message.getMessageId());
                        jsonObject.put("head", head);
                        jsonObject.put("body", message.messageContentAsString());
                        //发送文本格式
                        WebSocketFrame frame = new TextWebSocketFrame(jsonObject.toJSONString());
                        //发送二进制
                        //WebSocketFrame frame = new BinaryWebSocketFrame(Unpooled.wrappedBuffer(jsonObject.toJSONString().getBytes()));
                        out.add(frame);
                    }
                })

                .addLast(new IdleStateHandler(timeoutMillis, timeoutMillis, timeoutMillis, TimeUnit.MILLISECONDS))
                .addLast(new NetkitHandler(context));
    }

}
